package com.halachawiki.client.datepicker;

import java.util.Date;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.halachawiki.shared.ApproxDate;
import com.halachawiki.shared.ApproxDate.Accuracy;

public class DatePicker extends VerticalPanel {
	private static final String className = "com.halachawiki.client.DatePicker";
	private static final int JEWISH_TO_COMMON = (5771-2011);
	private static final int LOW_MARK = -4000;
	@SuppressWarnings("deprecation")
	private static final int HIGH_MARK = (new Date()).getYear() + 1900;
	private static final DateConstants Constants = GWT.create(DateConstants.class);
	private static final DateMessages Messages = GWT.create(DateMessages.class);
	private static int instances = 0;
	final int currentInstance = instances;
	
	private ApproxDate value;
	
	final RadioButton yearButton = new RadioButton(className+currentInstance, Constants.year());
	final RadioButton yearRangeButton = new RadioButton(className+currentInstance, Constants.fromYear());

	final TextBox yearTB = new TextBox();
	final Label yearError = new Label();
	final TextBox fromYearTB = new TextBox();
	final TextBox toYearTB = new TextBox();
	final Label yearRangeError = new Label();
	final CheckBox fromCreation = new CheckBox(Constants.fromCreation());

	
	public DatePicker() {
		++instances;
		
		// Build hierarchy
		HorizontalPanel hp1 = new HorizontalPanel();
		add(hp1);
		
		hp1.add(yearButton);		
		hp1.add(yearTB);
		hp1.add(yearError);
		
		HorizontalPanel hp2 = new HorizontalPanel();
		add(hp2);
		
		hp2.add(yearRangeButton);
		
		hp2.add(fromYearTB);
		Label toYearLabel = new Label(Constants.toYear()); 
		hp2.add(toYearLabel);
		hp2.add(toYearTB);
		hp2.add(yearRangeError);

		add(fromCreation);
		
		// CSS styles
		hp1.addStyleName("datepicker-hp");
		hp2.addStyleName("datepicker-hp");
		hp1.setSpacing(2);
		hp2.setSpacing(2);
		yearButton.addStyleName("datepicker-label");
		yearRangeButton.addStyleName("datepicker-label");
		toYearLabel.addStyleName("datepicker-label");
		yearError.addStyleName("datepicker-label");
		yearError.addStyleName("error-label");
		yearRangeError.addStyleName("datepicker-label");
		yearRangeError.addStyleName("error-label");
		yearTB.addStyleName("datepicker-textbox");
		fromYearTB.addStyleName("datepicker-textbox");
		toYearTB.addStyleName("datepicker-textbox");

		// Behaviors - radio button
		yearButton.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
			@Override
			public void onValueChange(ValueChangeEvent<Boolean> event) {
				fromYearTB.setEnabled(false);
				toYearTB.setEnabled(false);
				yearRangeError.setText("");
				yearTB.setEnabled(true);
				yearTB.setFocus(true);
				updateYearValue();
			} 	
		});
		yearRangeButton.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
			@Override
			public void onValueChange(ValueChangeEvent<Boolean> event) {
				yearTB.setEnabled(false);
				yearError.setText("");
				fromYearTB.setEnabled(true);
				toYearTB.setEnabled(true);
				fromYearTB.setFocus(true);
				updateYearRangeValue();
			}
		});
		yearTB.addFocusHandler(new FocusHandler() {
			@Override
			public void onFocus(FocusEvent event) {
				yearButton.setValue(true, true);
			} 
		});
		yearTB.addValueChangeHandler(new ValueChangeHandler<String>() {
			@Override
			public void onValueChange(ValueChangeEvent<String> event) {
				updateYearValue();
			} 
		});
		yearTB.addKeyUpHandler(new KeyUpHandler() {
			@Override
			public void onKeyUp(KeyUpEvent event) {
				updateYearValue();
			}
		});
		fromYearTB.addFocusHandler(new FocusHandler() {
			@Override
			public void onFocus(FocusEvent event) {
				yearRangeButton.setValue(true, true);
			}
		});
		fromYearTB.addValueChangeHandler(new ValueChangeHandler<String>() {
			@Override
			public void onValueChange(ValueChangeEvent<String> event) {
				updateYearRangeValue();
			} 
		});
		fromYearTB.addKeyUpHandler(new KeyUpHandler() {
			@Override
			public void onKeyUp(KeyUpEvent event) {
				updateYearRangeValue();
			}
		});
		toYearTB.addFocusHandler(new FocusHandler() {
			@Override
			public void onFocus(FocusEvent event) {
				yearRangeButton.setValue(true, true);
			}
		});
		toYearTB.addValueChangeHandler(new ValueChangeHandler<String>() {
			@Override
			public void onValueChange(ValueChangeEvent<String> event) {
				updateYearRangeValue();
			} 
		});
		toYearTB.addKeyUpHandler(new KeyUpHandler() {
			@Override
			public void onKeyUp(KeyUpEvent event) {
				updateYearRangeValue();
			}
		});
		fromCreation.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
			@Override
			public void onValueChange(ValueChangeEvent<Boolean> event) {
				if (yearButton.getValue().booleanValue()) {
					updateYearValue();
				}
				if (yearRangeButton.getValue().booleanValue()) {
					updateYearRangeValue();
				}
			}
		});
		
	}
	
	public ApproxDate getDate() {
		return value;
	}
	
	public void setDate(ApproxDate date) {
		value = date;
		if (value == null) return;
		if (value.getAccuracy().equals(Accuracy.YEAR)) {
			yearTB.setValue(String.valueOf(value.getStartYear()));
			yearButton.setValue(true, true);
		} else if (value.getAccuracy().equals(Accuracy.YEAR_RANGE)) {
			fromYearTB.setValue(String.valueOf(value.getStartYear()));
			toYearTB.setValue(String.valueOf(value.getEndYear()));
			yearRangeButton.setValue(true, true);
		}
	}
	
	enum NumberError {
		OK,
		EMPTY,
		INVALID_NUMBER,
		JEWISH_NEGATIVE,
		TOO_LOW,
		TOO_HIGH
	}
	
	NumberError validateYear(String value) {
		boolean isJewish = fromCreation.getValue();
		value = value.trim();
		if (value.isEmpty()) {
			return NumberError.EMPTY;
		}
		if (!value.matches("^\\-?[0-9]+$")) {
			return NumberError.INVALID_NUMBER;
		}
		int year = Integer.parseInt(value);
		if (isJewish) { 
			if (year < 0)
				return NumberError.JEWISH_NEGATIVE;
			year -= JEWISH_TO_COMMON;
		}
		if (year < LOW_MARK)
			return NumberError.TOO_LOW;
		if (year > HIGH_MARK)
			return NumberError.TOO_HIGH;
		return NumberError.OK;
	}
	
	int getYear(String value) {
		boolean isJewish = fromCreation.getValue();
		int year = Integer.parseInt(value.trim());
		if (isJewish) {
			year -= JEWISH_TO_COMMON;
		}
		return year;
	}
	
	void updateYearValue() {
		value = null;
		yearError.setText("");
		NumberError status = validateYear(yearTB.getText());
		if (status != NumberError.OK) {
			if (status == NumberError.INVALID_NUMBER)
				yearError.setText(Messages.invalidNumber(Constants.theYear()));
			if (status == NumberError.JEWISH_NEGATIVE)
				yearError.setText(Messages.jewishYearMustBePositive(Constants.theYear()));
			if (status == NumberError.TOO_HIGH)
				yearError.setText(Messages.yearTooHigh(Constants.theYear()));
			if (status == NumberError.TOO_LOW)
				yearError.setText(Messages.yearTooLow(Constants.theYear()));
			return;
		}
		yearError.setText("");
		int year = getYear(yearTB.getText());
		value = new ApproxDate(year);
	}
	
	NumberError updateYearRangeValueHelper(String value, String numberMessage) {
		NumberError status = validateYear(value);
		if (status != NumberError.OK) {
			if (status == NumberError.INVALID_NUMBER)
				yearRangeError.setText(Messages.invalidNumber(numberMessage));
			if (status == NumberError.JEWISH_NEGATIVE)
				yearRangeError.setText(Messages.jewishYearMustBePositive(numberMessage));
			if (status == NumberError.TOO_HIGH)
				yearRangeError.setText(Messages.yearTooHigh(numberMessage));
			if (status == NumberError.TOO_LOW)
				yearRangeError.setText(Messages.yearTooLow(numberMessage));
		}
		return status;
	}
	
	void updateYearRangeValue() {
		value = null;
		yearRangeError.setText("");
		NumberError firstStatus = updateYearRangeValueHelper(fromYearTB.getText(), Constants.fromYearError());
		NumberError secondStatus = updateYearRangeValueHelper(toYearTB.getText(), Constants.toYearError());
		if (!yearRangeError.getText().isEmpty())
			return;
		if (firstStatus == NumberError.EMPTY && !toYearTB.getText().trim().isEmpty())
			yearRangeError.setText(Constants.fillFromYear());
		if (firstStatus != NumberError.OK)
			return;
		if (secondStatus == NumberError.EMPTY)
			yearRangeError.setText(Constants.fillToYear());
		if (secondStatus != NumberError.OK)
			return;
		int fromYear = getYear(fromYearTB.getText());
		int toYear = getYear(toYearTB.getText());
		if (fromYear > toYear) {
			yearRangeError.setText(Constants.fromAfterTo());
			return;
		}
		yearRangeError.setText("");
		value = new ApproxDate(fromYear, toYear);
	}	
}
